#定义乘法层MulLayer类，来实现乘法层
class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None
        
    #定义前向传播
    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y

        return out

    #定义后向传播：将从上游传来的导数（dout）乘以正向传播的翻转值
    def backward(self, dout):
        #翻转x和y
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy

#使用MulLayer实现购买苹果的例子（购买2个单价为100元的苹果，消费税为1.1）
apple = 100
apple_num = 2
tax = 1.1
#实例化苹果乘法层和消费税乘法层
mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

#前向传播
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)
print('price: ' + str(price))

#后向传播：求出各个变量的导数
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print('dapple: %10.3f , dapple_num: %10.2f , dtax: %d .' % (dapple, dapple_num, dtax))

#定义加法层AddLayer类，来实现加法层
class AddLayer:
    #加法层不需要特意进行初始化
    def __init__(self):
        pass

    #定义前向传播：接收x和y两个参数，将它们相加后输出
    def forward(self, x, y):
        out = x + y
        return out

    #定义后向传播：将上游传来的导数（dout）原封不动地传递给下游
    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1
        return dx, dy

#使用加法层AddLayer类和乘法层MulLayer类实现购买2个苹果和3个橘子的例子（苹果单价100元消费税1.1，橘子单价150元消费税1.1）
apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

#实例化层
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

#前向传播
apple_price = mul_apple_layer.forward(apple, apple_num)  #step1
orange_price = mul_orange_layer.forward(orange, orange_num)  #step2
all_price = add_apple_orange_layer.forward(apple_price, orange_price)  #step3
price = mul_tax_layer.forward(all_price, tax)  #step4
print('price: %10.2f' % price)

#后向传播
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)  #step4
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)  #step3
dorange, dorange_num = mul_orange_layer.backward(dorange_price)  #step2
dapple, dapple_num = mul_apple_layer.backward(dapple_price)  #step1
print('dapple_num: %10.2f, dapple: %10.2f, dorange: %10.2f, dorange_num: %10.2f, dtax: %10.2f' % (dapple_num, dapple, dorange, dorange_num, dtax))
